package com.ruoyi.common.utils.baidu;

import com.alibaba.fastjson2.JSONObject;
import com.ruoyi.common.utils.StringUtils;
import org.springframework.web.util.UriUtils;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLEncoder;
import java.security.NoSuchAlgorithmException;
import java.time.Duration;
import java.time.LocalDateTime;
import java.util.LinkedHashMap;
import java.util.Map;

/**
 * @author: jkk
 * @create: 2025/3/2-0:04
 * @Description:
 */
public class BaiduMapUtil {
    public static String AK = "mw5cnLP2uVIhlGgLlrUAeRzsGRYH34Yx";

    public static String SK = "oxxzHltVPmkaT1V8PkS022yuvAixB1mw";

    // 地球半径（单位：公里）
    private static final double EARTH_RADIUS = 6371.0;

    public static String URL = "https://api.map.baidu.com/weather/v1/?";

    public static void main(String[] args) throws Exception {

        rerseveGeoCode("23.24816406","91.10794141");
        //getWeather("310101");
        //getWeatherAbroad("JPN10041030001");
        //coordinateConversion("68.26488781","145.0170517");
    }


    /**
     * 选择了ak，使用SN校验：
     * 根据您选择的AK已为您生成调用代码
     * 检测您当前的AK设置了sn检验，本示例中已为您生成sn计算代码
     * @param lat
     * @param lng
     * @throws Exception
     */
    public static String rerseveGeoCode(String lat,String lng) throws Exception {
        String strUrl = "https://api.map.baidu.com/reverse_geocoding/v3/?";
        Map<String, String> param = new LinkedHashMap<>();
        param.put("ak", AK);
        param.put("output", "json");
        param.put("coordtype", "bd09ll");
        param.put("extensions_poi", "0");
        param.put("location", lat+","+lng);
        param.put("language", "zh-CN");
        param.put("sn", rerseveGeoCodeSn(lat, lng));
        StringBuffer queryString = new StringBuffer();
        queryString.append(strUrl);
        for (Map.Entry<?, ?> pair : param.entrySet()) {
            queryString.append(pair.getKey() + "=");
            //    第一种方式使用的 jdk 自带的转码方式  第二种方式使用的 spring 的转码方法 两种均可
            //    queryString.append(URLEncoder.encode((String) pair.getValue(), "UTF-8").replace("+", "%20") + "&");
            queryString.append(UriUtils.encode((String) pair.getValue(), "UTF-8") + "&");
        }

        if (queryString.length() > 0) {
            queryString.deleteCharAt(queryString.length() - 1);
        }

        java.net.URL url = new URL(queryString.toString());
        System.out.println(queryString.toString());
        URLConnection httpConnection = (HttpURLConnection) url.openConnection();
        httpConnection.connect();

        InputStreamReader isr = new InputStreamReader(httpConnection.getInputStream());
        BufferedReader reader = new BufferedReader(isr);
        StringBuffer buffer = new StringBuffer();
        String line;
        while ((line = reader.readLine()) != null) {
            buffer.append(line);
        }
        reader.close();
        isr.close();
        System.out.println("SN: " + buffer.toString());
        JSONObject ret = new JSONObject();
        if(StringUtils.isNotEmpty(buffer.toString())){
            JSONObject jsonObject = JSONObject.parseObject(buffer.toString());
            if(jsonObject.getInteger("status") == 0){
                JSONObject result = jsonObject.getJSONObject("result");
                if(result != null){
                    JSONObject addressComponent = result.getJSONObject("addressComponent");
                    if(addressComponent != null){
                        String adcode = addressComponent.getString("adcode");
                        ret.put("adcode", adcode);
                    }
                    ret.put("address", result.getString("formatted_address"));
                    return ret.toString();
                }
            }
        }
        return "";
    }

    public static String rerseveGeoCodeSn(String lat, String lng) throws UnsupportedEncodingException,
            NoSuchAlgorithmException {
        BaiduMapUtil snCal = new BaiduMapUtil();
        // 计算sn跟参数对出现顺序有关，get请求请使用LinkedHashMap保存<key,value>，该方法根据key的插入顺序排序；post请使用TreeMap保存<key,value>，该方法会自动将key按照字母a-z顺序排序。
        // 所以get请求可自定义参数顺序（sn参数必须在最后）发送请求，但是post请求必须按照字母a-z顺序填充body（sn参数必须在最后）。
        // 以get请求为例：http://api.map.baidu.com/geocoder/v2/?address=百度大厦&output=json&ak=yourak，paramsMap中先放入address，再放output，然后放ak，放入顺序必须跟get请求中对应参数的出现顺序保持一致。
        Map paramsMap = new LinkedHashMap<String, String>();
        paramsMap.put("ak", AK);
        paramsMap.put("output", "json");
        paramsMap.put("coordtype", "bd09ll");
        paramsMap.put("extensions_poi", "0");
        paramsMap.put("location", lat+","+lng);
        paramsMap.put("language", "zh-CN");
        // 调用下面的toQueryString方法，对LinkedHashMap内所有value作utf8编码，拼接返回结果address=%E7%99%BE%E5%BA%A6%E5%A4%A7%E5%8E%A6&output=json&ak=yourak
        String paramsStr = snCal.toQueryString(paramsMap);
        // 对paramsStr前面拼接上/geocoder/v2/?，后面直接拼接yoursk得到/geocoder/v2/?address=%E7%99%BE%E5%BA%A6%E5%A4%A7%E5%8E%A6&output=json&ak=yourakyoursk
        String wholeStr = new String("/reverse_geocoding/v3/?" + paramsStr + SK);
        System.out.println(wholeStr);
        // 对上面wholeStr再作utf8编码
        String tempStr = URLEncoder.encode(wholeStr, "UTF-8");
        // 调用下面的MD5方法得到最后的sn签名
        String sn = snCal.MD5(tempStr);
        System.out.println(sn);
        return sn;
    }

    public static String coordinateConversion(String lat,String lng) throws Exception {
        String strUrl = "https://api.map.baidu.com/geoconv/v2/?";
        Map<String, String> param = new LinkedHashMap<>();
        param.put("coords", lng+","+lat);
        param.put("model", "2");
        param.put("ak", AK);
        param.put("sn", coordinateConversionSn(lat, lng));
        StringBuffer queryString = new StringBuffer();
        queryString.append(strUrl);
        for (Map.Entry<?, ?> pair : param.entrySet()) {
            queryString.append(pair.getKey() + "=");
            //    第一种方式使用的 jdk 自带的转码方式  第二种方式使用的 spring 的转码方法 两种均可
            //    queryString.append(URLEncoder.encode((String) pair.getValue(), "UTF-8").replace("+", "%20") + "&");
            queryString.append(UriUtils.encode((String) pair.getValue(), "UTF-8") + "&");
        }

        if (queryString.length() > 0) {
            queryString.deleteCharAt(queryString.length() - 1);
        }

        java.net.URL url = new URL(queryString.toString());
        System.out.println(queryString.toString());
        URLConnection httpConnection = (HttpURLConnection) url.openConnection();
        httpConnection.connect();

        InputStreamReader isr = new InputStreamReader(httpConnection.getInputStream());
        BufferedReader reader = new BufferedReader(isr);
        StringBuffer buffer = new StringBuffer();
        String line;
        while ((line = reader.readLine()) != null) {
            buffer.append(line);
        }
        reader.close();
        isr.close();
        System.out.println("SN: " + buffer.toString());
        if(StringUtils.isNotEmpty(buffer.toString())){
            JSONObject jsonObject = JSONObject.parseObject(buffer.toString());
            if(jsonObject.getInteger("status") == 0){
                JSONObject result = jsonObject.getJSONArray("result").getJSONObject(0);
                return result.toString();
            }
        }
        return "";
    }

    public static String coordinateConversionSn(String lat, String lng) throws UnsupportedEncodingException,
            NoSuchAlgorithmException {
        BaiduMapUtil snCal = new BaiduMapUtil();
        // 计算sn跟参数对出现顺序有关，get请求请使用LinkedHashMap保存<key,value>，该方法根据key的插入顺序排序；post请使用TreeMap保存<key,value>，该方法会自动将key按照字母a-z顺序排序。
        // 所以get请求可自定义参数顺序（sn参数必须在最后）发送请求，但是post请求必须按照字母a-z顺序填充body（sn参数必须在最后）。
        // 以get请求为例：http://api.map.baidu.com/geocoder/v2/?address=百度大厦&output=json&ak=yourak，paramsMap中先放入address，再放output，然后放ak，放入顺序必须跟get请求中对应参数的出现顺序保持一致。
        Map paramsMap = new LinkedHashMap<String, String>();
        paramsMap.put("coords", lng+","+lat);
        paramsMap.put("model", "2");
        paramsMap.put("ak", AK);
        // 调用下面的toQueryString方法，对LinkedHashMap内所有value作utf8编码，拼接返回结果address=%E7%99%BE%E5%BA%A6%E5%A4%A7%E5%8E%A6&output=json&ak=yourak
        String paramsStr = snCal.toQueryString(paramsMap);
        // 对paramsStr前面拼接上/geocoder/v2/?，后面直接拼接yoursk得到/geocoder/v2/?address=%E7%99%BE%E5%BA%A6%E5%A4%A7%E5%8E%A6&output=json&ak=yourakyoursk
        String wholeStr = new String("/geoconv/v2/?" + paramsStr + SK);
        System.out.println(wholeStr);
        // 对上面wholeStr再作utf8编码
        String tempStr = URLEncoder.encode(wholeStr, "UTF-8");
        // 调用下面的MD5方法得到最后的sn签名
        String sn = snCal.MD5(tempStr);
        System.out.println(sn);
        return sn;
    }
    public static String getWeatherSn(String adcode) throws UnsupportedEncodingException,
            NoSuchAlgorithmException {
        BaiduMapUtil snCal = new BaiduMapUtil();
        // 计算sn跟参数对出现顺序有关，get请求请使用LinkedHashMap保存<key,value>，该方法根据key的插入顺序排序；post请使用TreeMap保存<key,value>，该方法会自动将key按照字母a-z顺序排序。
        // 所以get请求可自定义参数顺序（sn参数必须在最后）发送请求，但是post请求必须按照字母a-z顺序填充body（sn参数必须在最后）。
        // 以get请求为例：http://api.map.baidu.com/geocoder/v2/?address=百度大厦&output=json&ak=yourak，paramsMap中先放入address，再放output，然后放ak，放入顺序必须跟get请求中对应参数的出现顺序保持一致。
        Map paramsMap = new LinkedHashMap<String, String>();
        paramsMap.put("district_id", adcode);
        paramsMap.put("data_type", "all");
        paramsMap.put("ak", AK);
        // 调用下面的toQueryString方法，对LinkedHashMap内所有value作utf8编码，拼接返回结果address=%E7%99%BE%E5%BA%A6%E5%A4%A7%E5%8E%A6&output=json&ak=yourak
        String paramsStr = snCal.toQueryString(paramsMap);
        // 对paramsStr前面拼接上/geocoder/v2/?，后面直接拼接yoursk得到/geocoder/v2/?address=%E7%99%BE%E5%BA%A6%E5%A4%A7%E5%8E%A6&output=json&ak=yourakyoursk
        String wholeStr = new String("/weather/v1/?" + paramsStr + SK);
        System.out.println(wholeStr);
        // 对上面wholeStr再作utf8编码
        String tempStr = URLEncoder.encode(wholeStr, "UTF-8");
        // 调用下面的MD5方法得到最后的sn签名
        String sn = snCal.MD5(tempStr);
        //System.out.println(sn);
        return sn;
    }

    public static String getWeather(String adcode) throws Exception {
        String strUrl = "https://api.map.baidu.com/weather/v1/?";
        Map<String, String> param = new LinkedHashMap<>();
        param.put("district_id", adcode);
        param.put("data_type", "all");
        param.put("ak", AK);
        param.put("sn",getWeatherSn(adcode));

        StringBuffer queryString = new StringBuffer();
        queryString.append(strUrl);
        for (Map.Entry<?, ?> pair : param.entrySet()) {
            queryString.append(pair.getKey() + "=");
            //    第一种方式使用的 jdk 自带的转码方式  第二种方式使用的 spring 的转码方法 两种均可
            //    queryString.append(URLEncoder.encode((String) pair.getValue(), "UTF-8").replace("+", "%20") + "&");
            queryString.append(UriUtils.encode((String) pair.getValue(), "UTF-8") + "&");
        }

        if (queryString.length() > 0) {
            queryString.deleteCharAt(queryString.length() - 1);
        }

        java.net.URL url = new URL(queryString.toString());
        System.out.println(queryString.toString());
        URLConnection httpConnection = (HttpURLConnection) url.openConnection();
        httpConnection.connect();

        InputStreamReader isr = new InputStreamReader(httpConnection.getInputStream());
        BufferedReader reader = new BufferedReader(isr);
        StringBuffer buffer = new StringBuffer();
        String line;
        while ((line = reader.readLine()) != null) {
            buffer.append(line);
        }
        reader.close();
        isr.close();
        //System.out.println("SN: " + buffer.toString());
        if(StringUtils.isNotEmpty(buffer.toString())){
            JSONObject jsonObject = JSONObject.parseObject(buffer.toString());
            if(jsonObject.getInteger("status") == 0){
                JSONObject result = jsonObject.getJSONObject("result");
                JSONObject now = result.getJSONObject("now");
                if(now != null){
                    String temperature = now.getString("temp");
                    String text = now.getString("text");
                    String wind_class = now.getString("wind_class");
                    String wind_dir = now.getString("wind_dir");
                    return text + "，温度：" + temperature + "，风向：" + wind_dir + "，风力：" + wind_class;
                }
            }
        }
        return "";
    }

    public static String getWeatherAbroadSn(String adcode) throws UnsupportedEncodingException,
            NoSuchAlgorithmException {
        BaiduMapUtil snCal = new BaiduMapUtil();
        // 计算sn跟参数对出现顺序有关，get请求请使用LinkedHashMap保存<key,value>，该方法根据key的插入顺序排序；post请使用TreeMap保存<key,value>，该方法会自动将key按照字母a-z顺序排序。
        // 所以get请求可自定义参数顺序（sn参数必须在最后）发送请求，但是post请求必须按照字母a-z顺序填充body（sn参数必须在最后）。
        // 以get请求为例：http://api.map.baidu.com/geocoder/v2/?address=百度大厦&output=json&ak=yourak，paramsMap中先放入address，再放output，然后放ak，放入顺序必须跟get请求中对应参数的出现顺序保持一致。
        Map paramsMap = new LinkedHashMap<String, String>();
        paramsMap.put("district_id", adcode);
        paramsMap.put("data_type", "all");
        paramsMap.put("ak", AK);
        // 调用下面的toQueryString方法，对LinkedHashMap内所有value作utf8编码，拼接返回结果address=%E7%99%BE%E5%BA%A6%E5%A4%A7%E5%8E%A6&output=json&ak=yourak
        String paramsStr = snCal.toQueryString(paramsMap);
        // 对paramsStr前面拼接上/geocoder/v2/?，后面直接拼接yoursk得到/geocoder/v2/?address=%E7%99%BE%E5%BA%A6%E5%A4%A7%E5%8E%A6&output=json&ak=yourakyoursk
        String wholeStr = new String("/weather_abroad/v1/?" + paramsStr + SK);
        System.out.println(wholeStr);
        // 对上面wholeStr再作utf8编码
        String tempStr = URLEncoder.encode(wholeStr, "UTF-8");
        // 调用下面的MD5方法得到最后的sn签名
        String sn = snCal.MD5(tempStr);
        System.out.println(sn);
        return sn;
    }

    public static String getWeatherAbroad(String adcode) throws Exception {
        String strUrl = "https://api.map.baidu.com/weather_abroad/v1/?";
        Map<String, String> param = new LinkedHashMap<>();
        param.put("district_id", adcode);
        param.put("data_type", "all");
        param.put("ak", AK);
        param.put("sn",getWeatherAbroadSn(adcode));

        StringBuffer queryString = new StringBuffer();
        queryString.append(strUrl);
        for (Map.Entry<?, ?> pair : param.entrySet()) {
            queryString.append(pair.getKey() + "=");
            //    第一种方式使用的 jdk 自带的转码方式  第二种方式使用的 spring 的转码方法 两种均可
            //    queryString.append(URLEncoder.encode((String) pair.getValue(), "UTF-8").replace("+", "%20") + "&");
            queryString.append(UriUtils.encode((String) pair.getValue(), "UTF-8") + "&");
        }

        if (queryString.length() > 0) {
            queryString.deleteCharAt(queryString.length() - 1);
        }

        java.net.URL url = new URL(queryString.toString());
        System.out.println(queryString.toString());
        URLConnection httpConnection = (HttpURLConnection) url.openConnection();
        httpConnection.connect();

        InputStreamReader isr = new InputStreamReader(httpConnection.getInputStream());
        BufferedReader reader = new BufferedReader(isr);
        StringBuffer buffer = new StringBuffer();
        String line;
        while ((line = reader.readLine()) != null) {
            buffer.append(line);
        }
        reader.close();
        isr.close();
        System.out.println("SN: " + buffer.toString());
        return buffer.toString();
    }

    // 对Map内所有value作utf8编码，拼接返回结果
    public String toQueryString(Map<?, ?> data)
            throws UnsupportedEncodingException {
        StringBuffer queryString = new StringBuffer();
        for (Map.Entry<?, ?> pair : data.entrySet()) {
            queryString.append(pair.getKey() + "=");
            //    第一种方式使用的 jdk 自带的转码方式  第二种方式使用的 spring 的转码方法 两种均可
            //    queryString.append(URLEncoder.encode((String) pair.getValue(), "UTF-8").replace("+", "%20") + "&");
            queryString.append(UriUtils.encode((String) pair.getValue(), "UTF-8") + "&");
        }
        if (queryString.length() > 0) {
            queryString.deleteCharAt(queryString.length() - 1);
        }
        return queryString.toString();
    }
    // 来自stackoverflow的MD5计算方法，调用了MessageDigest库函数，并把byte数组结果转换成16进制
    public String MD5(String md5) {
        try {
            java.security.MessageDigest md = java.security.MessageDigest
                    .getInstance("MD5");
            byte[] array = md.digest(md5.getBytes());
            StringBuffer sb = new StringBuffer();
            for (int i = 0; i < array.length; ++i) {
                sb.append(Integer.toHexString((array[i] & 0xFF) | 0x100)
                        .substring(1, 3));
            }
            return sb.toString();
        } catch (NoSuchAlgorithmException e) {
        }
        return null;
    }

    /**
     * 将角度转换为弧度。
     */
    private static double deg2rad(double deg) {
        return deg * (Math.PI / 180);
    }

    /**
     * 计算两个经纬度点之间的距离。
     *
     * @param lat1 点1纬度
     * @param lon1 点1经度
     * @param lat2 点2纬度
     * @param lon2 点2经度
     * @return 两点之间的距离，单位为公里
     */
    public static double calculateDistance(double lat1, double lon1, double lat2, double lon2) {
        double dLat = deg2rad(lat2 - lat1);
        double dLon = deg2rad(lon2 - lon1);

        double a = Math.sin(dLat / 2) * Math.sin(dLat / 2) +
                Math.cos(deg2rad(lat1)) * Math.cos(deg2rad(lat2)) *
                        Math.sin(dLon / 2) * Math.sin(dLon / 2);

        double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));

        return EARTH_RADIUS * c; // 返回的距离单位是公里
    }

    /**
     * 计算平均速度。
     *
     * @param distance 距离，单位为公里
     * @param startTime 开始时间
     * @param endTime 结束时间
     * @return 平均速度，单位为公里/小时
     */
    public static double calculateAverageSpeed(double distance, LocalDateTime startTime, LocalDateTime endTime) {
        long durationInSeconds = Duration.between(startTime, endTime).getSeconds();
        double durationInHours = durationInSeconds / 3600.0;
        return distance / durationInHours;
    }
}
